#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
串口通信模块
Serial Communication Module

用于处理串口数据接收和唤醒信号检测
Handles serial data reception and wake signal detection
"""

import serial
import threading
import time
import queue
import logging
from serial import EIGHTBITS, PARITY_NONE, STOPBITS_ONE

class SerialCommunicator:
    def __init__(self, port='/dev/myspeech', baudrate=115200, wake_callback=None):
        """
        初始化串口通信器
        Initialize serial communicator
        
        Args:
            port (str): 串口设备路径 / Serial device path
            baudrate (int): 波特率 / Baud rate
            wake_callback (callable): 唤醒回调函数 / Wake callback function
        """
        self.port = port
        self.baudrate = baudrate
        self.wake_callback = wake_callback
        self.ser = None
        self.running = False
        self.receive_thread = None
        self.wake_queue = queue.Queue()
        
        # 设置日志
        self.logger = logging.getLogger(__name__)
        if not self.logger.handlers:
            handler = logging.StreamHandler()
            formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
            handler.setFormatter(formatter)
            self.logger.addHandler(handler)
            self.logger.setLevel(logging.INFO)
        
        # 数据缓冲区 - 使用更大的缓冲区来避免数据丢失
        self.data_buffer = bytearray()
        self.max_buffer_size = 1024  # 最大缓冲区大小
        
        # 唤醒序列模式
        self.wake_patterns = [
            bytes([0xAA, 0x55, 0x01, 0x00, 0xFB]),
            bytes([0xAA, 0x55, 0x02, 0x00, 0xFB]),
            bytes([0xAA, 0x55, 0x03, 0x00, 0xFB]),
            bytes([0xAA, 0x55, 0x04, 0x00, 0xFB]),
            bytes([0xAA, 0x55, 0x05, 0x00, 0xFB]),
            bytes([0xAA, 0x55, 0x06, 0x00, 0xFB]),
            bytes([0x55, 0xAA, 0x01, 0x00, 0xFB]),
            bytes([0x55, 0xAA, 0x02, 0x00, 0xFB]),
            bytes([0x55, 0xAA, 0x03, 0x00, 0xFB]),
            bytes([0x55, 0xAA, 0x04, 0x00, 0xFB]),
            bytes([0x55, 0xAA, 0x05, 0x00, 0xFB]),
            bytes([0x55, 0xAA, 0x06, 0x00, 0xFB])
        ]
        
        self.logger.info(f"SerialCommunicator initialized for port: {port}")
    
    def open_port(self):
        """
        打开串口
        Open serial port
        
        Returns:
            bool: 是否成功打开 / Whether successfully opened
        """
        try:
            if self.ser and self.ser.is_open:
                self.logger.info(f"Port {self.port} is already open")
                return True
            
            self.ser = serial.Serial(
                port=self.port,
                baudrate=self.baudrate,
                bytesize=EIGHTBITS,
                parity=PARITY_NONE,
                stopbits=STOPBITS_ONE,
                timeout=0.1,
                xonxoff=False,
                rtscts=False,
                dsrdtr=False
            )
            
            if self.ser.is_open:
                # 清空输入输出缓冲区
                self.ser.reset_input_buffer()
                self.ser.reset_output_buffer()
                
                self.logger.info(f"Serial port {self.port} opened successfully")
                self.logger.info(f"Port config: {self.baudrate} 8N1, timeout: {self.ser.timeout}s")
                return True
            else:
                self.logger.error(f"Failed to open serial port {self.port}")
                return False
                
        except Exception as e:
            self.logger.error(f"Error opening serial port {self.port}: {e}")
            return False
    
    def close_port(self):
        """
        关闭串口
        Close serial port
        """
        try:
            if self.ser and self.ser.is_open:
                self.ser.close()
                self.logger.info(f"Serial port {self.port} closed")
        except Exception as e:
            self.logger.error(f"Error closing serial port: {e}")
    
    def start_receiving(self):
        """
        开始接收数据
        Start receiving data
        
        Returns:
            bool: 是否成功启动 / Whether successfully started
        """
        if not self.open_port():
            return False
        
        if self.running:
            self.logger.warning("Data receiving is already running")
            return True
        
        self.running = True
        self.receive_thread = threading.Thread(target=self._receive_data, daemon=True)
        self.receive_thread.start()
        
        self.logger.info("Serial data receiving started")
        return True
    
    def stop_receiving(self):
        """
        停止接收数据
        Stop receiving data
        """
        self.running = False
        
        if self.receive_thread and self.receive_thread.is_alive():
            self.receive_thread.join(timeout=2.0)
            if self.receive_thread.is_alive():
                self.logger.warning("Receive thread did not stop gracefully")
        
        self.close_port()
        self.logger.info("Serial data receiving stopped")
    
    def _receive_data(self):
        """
        数据接收线程函数
        Data receiving thread function
        
        检测唤醒信号序列: 0xAA 0x55 [0x01-0x06] 0x00 0xFB 或 0x55 0xAA [0x01-0x06] 0x00 0xFB
        Detects wake-up signal sequence: 0xAA 0x55 [0x01-0x06] 0x00 0xFB or 0x55 0xAA [0x01-0x06] 0x00 0xFB
        """
        self.logger.info("Serial data receiving thread started")
        last_reconnect_time = 0
        no_data_count = 0
        total_bytes_received = 0
        
        while self.running:
            if self.ser and self.ser.is_open:
                try:
                    # 使用阻塞模式读取数据 - 基于测试结果，阻塞模式能正确读取完整数据包
                    # 尝试读取5字节的完整唤醒序列数据包
                    data = self.ser.read(5)  # 阻塞读取5字节，超时由串口timeout控制
                    
                    if data:
                        no_data_count = 0
                        total_bytes_received += len(data)
                        
                        # 记录接收到的数据
                        data_hex = ' '.join([f'{b:02X}' for b in data])
                        self.logger.info(f"[RX] Received {len(data)} bytes: [{data_hex}]")
                        
                        # 添加到缓冲区
                        self.data_buffer.extend(data)
                        
                        # 显示当前缓冲区内容（用于调试）
                        if len(self.data_buffer) >= 5:
                            buffer_hex = ' '.join([f'{b:02X}' for b in self.data_buffer[-10:]])  # 显示最后10个字节
                            self.logger.info(f"[BUFFER] Last 10 bytes: [{buffer_hex}]")
                        
                        # 限制缓冲区大小
                        if len(self.data_buffer) > self.max_buffer_size:
                            # 保留最后的数据，丢弃最早的数据
                            excess = len(self.data_buffer) - self.max_buffer_size
                            self.data_buffer = self.data_buffer[excess:]
                            self.logger.warning(f"Buffer overflow, removed {excess} bytes")
                        
                        # 检查唤醒序列
                        self._check_wake_patterns()
                        
                        # 每100字节输出一次统计信息
                        if total_bytes_received % 100 == 0:
                            self.logger.info(f"[STATS] Total received: {total_bytes_received} bytes, buffer size: {len(self.data_buffer)}")
                    
                    else:
                        # 超时未读取到数据
                        no_data_count += 1
                        if no_data_count % 100 == 0:  # 每100次超时时输出一次
                            self.logger.debug(f"Read timeout for {no_data_count} attempts")
                        
                        # 短暂休眠避免CPU占用过高
                        time.sleep(0.001)
                
                except Exception as e:
                    self.logger.error(f"Error receiving data: {e}")
                    # 尝试重新连接
                    current_time = time.time()
                    if current_time - last_reconnect_time > 5.0:  # 至少间隔5秒重连
                        self.logger.info("Attempting to reconnect due to serial error")
                        self._reconnect()
                        last_reconnect_time = current_time
                        # 清空缓冲区
                        self.data_buffer.clear()
                    
                    time.sleep(0.1)  # 错误后短暂休眠
            else:
                # 串口未打开，尝试重新打开
                if not self.open_port():
                    time.sleep(1.0)  # 重连失败后等待1秒
        
        self.logger.info("Serial data receiving thread stopped")
    
    def _check_wake_patterns(self):
        """
        检查缓冲区中的唤醒模式
        Check wake patterns in buffer
        """
        if len(self.data_buffer) < 5:
            return
        
        # 在缓冲区中搜索唤醒模式
        for pattern in self.wake_patterns:
            pattern_pos = self.data_buffer.find(pattern)
            if pattern_pos != -1:
                # 找到唤醒模式
                pattern_hex = ' '.join([f'{b:02X}' for b in pattern])
                self.logger.info(f"🎯 Wake pattern detected at position {pattern_pos}: [{pattern_hex}]")
                
                # 触发唤醒回调
                self.wake_queue.put("wake_up")
                if self.wake_callback:
                    try:
                        self.logger.info("Calling wake callback function")
                        self.wake_callback()
                    except Exception as e:
                        self.logger.error(f"Error in wake callback: {e}")
                else:
                    self.logger.warning("No wake callback function set")
                
                # 只移除模式之前的数据，保留模式本身用于调试
                if pattern_pos > 0:
                    self.data_buffer = self.data_buffer[pattern_pos:]
                    self.logger.info(f"Removed {pattern_pos} bytes before pattern from buffer, remaining: {len(self.data_buffer)}")
                else:
                    self.logger.info(f"Pattern found at buffer start, keeping all {len(self.data_buffer)} bytes")
                
                # 找到一个模式后就返回，避免重复触发
                return
    
    def _reconnect(self):
        """
        重新连接串口
        Reconnect serial port
        """
        try:
            self.close_port()
            time.sleep(0.5)  # 等待端口释放
            
            if self.open_port():
                self.logger.info("Serial port reconnected successfully")
            else:
                self.logger.error("Failed to reconnect serial port")
        except Exception as e:
            self.logger.error(f"Error during reconnection: {e}")
    
    def get_wake_signal(self, timeout=None):
        """
        获取唤醒信号（阻塞方式）
        Get wake signal (blocking)
        
        Args:
            timeout (float): 超时时间（秒）/ Timeout in seconds
        
        Returns:
            str: 唤醒信号 / Wake signal
        """
        try:
            return self.wake_queue.get(timeout=timeout)
        except queue.Empty:
            return None
    
    def has_wake_signal(self):
        """
        检查是否有唤醒信号（非阻塞）
        Check if there's a wake signal (non-blocking)
        
        Returns:
            bool: 是否有唤醒信号 / Whether there's a wake signal
        """
        return not self.wake_queue.empty()
    
    def clear_wake_signals(self):
        """
        清空所有唤醒信号
        Clear all wake signals
        """
        while not self.wake_queue.empty():
            try:
                self.wake_queue.get_nowait()
            except queue.Empty:
                break
    
    def send_data(self, data):
        """
        发送数据
        Send data
        
        Args:
            data (bytes): 要发送的数据 / Data to send
        
        Returns:
            bool: 是否发送成功 / Whether sent successfully
        """
        if not self.ser or not self.ser.is_open:
            self.logger.error("Serial port is not open")
            return False
        
        try:
            bytes_written = self.ser.write(data)
            self.ser.flush()
            
            data_hex = ' '.join([f'{b:02X}' for b in data])
            self.logger.info(f"[TX] Sent {bytes_written} bytes: [{data_hex}]")
            return bytes_written == len(data)
        
        except Exception as e:
            self.logger.error(f"Error sending data: {e}")
            return False
    
    def get_status(self):
        """
        获取串口状态
        Get serial port status
        
        Returns:
            dict: 状态信息 / Status information
        """
        return {
            'port': self.port,
            'baudrate': self.baudrate,
            'is_open': self.ser.is_open if self.ser else False,
            'running': self.running,
            'buffer_size': len(self.data_buffer),
            'wake_queue_size': self.wake_queue.qsize()
        }

# 测试函数
def test_serial_comm():
    """
    测试串口通信
    Test serial communication
    """
    def wake_callback():
        print("🎯 Wake up signal received!")
    
    comm = SerialCommunicator(wake_callback=wake_callback)
    
    if comm.start_receiving():
        print("Serial communication started. Waiting for wake signals...")
        print("Press Ctrl+C to stop")
        
        try:
            while True:
                # 检查唤醒信号
                if comm.has_wake_signal():
                    signal = comm.get_wake_signal()
                    print(f"Received wake signal: {signal}")
                
                # 输出状态
                status = comm.get_status()
                print(f"Status: {status}")
                
                time.sleep(10)
        
        except KeyboardInterrupt:
            print("\nStopping...")
        
        finally:
            comm.stop_receiving()
            print("Serial communication stopped")
    
    else:
        print("Failed to start serial communication")

if __name__ == "__main__":
    test_serial_comm()